package com.cbank.platform.weixin;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.ClientProtocolException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.cbank.platform.weixin.model.MD5;
import com.cbank.platform.weixin.model.WeixinPayOrderQueryResult;
import com.cbank.platform.weixin.model.WeixinPayTransferResult;
import com.cbank.platform.weixin.model.WeixinPayUnifiedOrderParams;
import com.cbank.platform.weixin.model.WeixinPayUnifiedOrderResult;
import com.cbank.utils.HttpClientUtils;
import com.cbank.utils.UniqueKeyGenerator;

public class WeixinPayCore {
	private static Log logger = LogFactory.getLog(WeixinPayCore.class);

	private final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

	/**
	 * 请求微信后台，生成预付单信息
	 * 
	 * @param params
	 * @throws IOException 
	 * @throws URISyntaxException 
	 * @throws ClientProtocolException 
	 */
	public static WeixinPayUnifiedOrderResult unifiedOrder(
			WeixinPayUnifiedOrderParams params) throws ClientProtocolException, URISyntaxException, IOException {
		logger.info(params.toString());
		
		Map<String, String> packageParams = new HashMap<String, String>(16);
		packageParams.put("appid", WeixinPayConfig.APP_ID);
		packageParams.put("body", params.getBody());
		packageParams.put("mch_id", WeixinPayConfig.MCH_ID);
		packageParams.put("nonce_str", UniqueKeyGenerator.generateToken());
		packageParams.put("notify_url", params.getNotifyUrl());
		packageParams.put("out_trade_no", params.getOutTradeNo());
		packageParams.put("spbill_create_ip", params.getSpbillCreateIp());
		packageParams.put("total_fee", String.valueOf(params.getTotalFee()));
		packageParams.put("trade_type", params.getTradeType());
		packageParams.put("time_stamp", String.valueOf(System.currentTimeMillis()/1000));
		packageParams.put("openid", params.getOpenid());
		String sign = genPackageSign(packageParams);
		packageParams.put("sign",sign);
		String resultStr = HttpClientUtils.simplePostXMLInvoke(UNIFIED_ORDER_URL, packageParams);
		logger.info(resultStr);
		WeixinPayUnifiedOrderResult result = xmlToUnifiedOrderResult(resultStr);
		return result;
	}
	

	/**
	 * @param resultStr
	 * @return
	 */
	private static WeixinPayUnifiedOrderResult xmlToUnifiedOrderResult(String resultStr) {
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(resultStr);
		} catch (DocumentException e) {
			logger.error(e.getMessage(),e);
			return null;
		}
		Element rootEle = doc.getRootElement();
		WeixinPayUnifiedOrderResult result = new WeixinPayUnifiedOrderResult();
		result.setReturnCode(rootEle.element("return_code").getText());
		result.setReturnMsg(rootEle.element("return_msg") == null ? StringUtils.EMPTY : rootEle.element("return_msg").getText());
		if (rootEle.element("appid") != null) {
			result.setAppId(rootEle.element("appid").getText());
		}
		if (rootEle.element("mch_id") != null) {
			result.setMchId(rootEle.element("mch_id").getText());
		}
		if (rootEle.element("nonce_str") != null) {
			result.setNonceStr(rootEle.element("nonce_str").getText());
		}
		if (rootEle.element("sign") != null) {
			result.setSign(rootEle.element("sign").getText());
		}
		if (rootEle.element("result_code") != null) {
			result.setResultCode(rootEle.element("result_code").getText());
		}
		if (rootEle.element("prepay_id") != null) {
			result.setPrepayId(rootEle.element("prepay_id").getText());
		}
		if (rootEle.element("trade_type") != null) {
			result.setTradeType(rootEle.element("trade_type").getText());
		}
		if (rootEle.element("err_code") != null) {
			result.setErrorCode(rootEle.element("err_code").getText());
		}
		if (rootEle.element("err_code_des") != null) {
			result.setErrorCodeDesc(rootEle.element("err_code_des").getText());
		}
		return result;
	}
	
	private static WeixinPayTransferResult xmlToTransferResult(String resultStr) {
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(resultStr);
		} catch (DocumentException e) {
			logger.error(e.getMessage(),e);
			return null;
		}
		Element rootEle = doc.getRootElement();
		WeixinPayTransferResult result = new WeixinPayTransferResult();
		result.setReturnCode(rootEle.element("return_code").getText());
		result.setReturnMsg(rootEle.element("return_msg").getText());
		if (rootEle.element("mch_appid") != null) {
			result.setAppId(rootEle.element("mch_appid").getText());
		}
		if (rootEle.element("mchid") != null) {
			result.setMchId(rootEle.element("mchid").getText());
		}
		if (rootEle.element("nonce_str") != null) {
			result.setNonceStr(rootEle.element("nonce_str").getText());
		}
		if (rootEle.element("sign") != null) {
			result.setSign(rootEle.element("sign").getText());
		}
		if (rootEle.element("result_code") != null) {
			result.setResultCode(rootEle.element("result_code").getText());
		}
		if (rootEle.element("partner_trade_no") != null) {
			result.setPartnerTradeNo(rootEle.element("partner_trade_no").getText());
		}
		if (rootEle.element("payment_no") != null) {
			result.setPaymentNo(rootEle.element("payment_no").getText());
		}
		if (rootEle.element("payment_time") != null) {
			result.setPaymentTime(rootEle.element("payment_time").getText());
		}
		if (rootEle.element("err_code") != null) {
			result.setErrorCode(rootEle.element("err_code").getText());
		}
		if (rootEle.element("err_code_des") != null) {
			result.setErrorCodeDesc(rootEle.element("err_code_des").getText());
		}
		return result;
	}

	public static WeixinPayOrderQueryResult xmlToOrderQueryResult(String resultStr) {
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(resultStr);
		} catch (DocumentException e) {
			logger.error(e.getMessage(),e);
			return null;
		}
		Element rootEle = doc.getRootElement();
		WeixinPayOrderQueryResult result = new WeixinPayOrderQueryResult();
		result.setReturnCode(rootEle.element("return_code").getText());
		result.setReturnMsg(rootEle.element("return_msg") == null ? StringUtils.EMPTY : rootEle.element("return_msg").getText());
		if (rootEle.element("appid") != null) {
			result.setAppId(rootEle.element("appid").getText());
		}
		if (rootEle.element("mch_id") != null) {
			result.setMchId(rootEle.element("mch_id").getText());
		}
		if (rootEle.element("nonce_str") != null) {
			result.setNonceStr(rootEle.element("nonce_str").getText());
		}
		if (rootEle.element("sign") != null) {
			result.setSign(rootEle.element("sign").getText());
		}
		if (rootEle.element("result_code") != null) {
			result.setResultCode(rootEle.element("result_code").getText());
		}
		if (rootEle.element("err_code") != null) {
			result.setErrorCode(rootEle.element("err_code").getText());
		}
		if (rootEle.element("err_code_des") != null) {
			result.setErrorCodeDesc(rootEle.element("err_code_des").getText());
		}
		if (rootEle.element("openid") != null) {
			result.setOpenid(rootEle.element("openid").getText());
		}
		if (rootEle.element("is_subscribe") != null) {
			result.setIsSubscribe(rootEle.element("is_subscribe").getText());
		}
		if (rootEle.element("trade_type") != null) {
			result.setTradeType(rootEle.element("trade_type").getText());
		}
		if (rootEle.element("trade_state") != null) {
			result.setTradeState(rootEle.element("trade_state").getText());
		}
		if (rootEle.element("bank_type") != null) {
			result.setBankType(rootEle.element("bank_type").getText());
		}
		if (rootEle.element("total_fee") != null) {
			result.setTotalFee(Integer.parseInt(rootEle.element("total_fee")
					.getText()));
		}
		if (rootEle.element("cash_fee") != null) {
			result.setCashFee(Integer.parseInt(rootEle.element("cash_fee")
					.getText()));
		}
		if (rootEle.element("transaction_id") != null) {
			result.setTransactionId(rootEle.element("transaction_id").getText());
		}
		if (rootEle.element("out_trade_no") != null) {
			result.setOutTradeNo(rootEle.element("out_trade_no").getText());
		}
		if (rootEle.element("time_end") != null) {
			result.setTimeEnd(rootEle.element("time_end").getText());
		}

		if (rootEle.element("trade_state_desc") != null) {
			result.setTradeStateDesc((rootEle.element("trade_state_desc")
					.getText()));
		}
		return result;
	}

	/**
	 * 生成签名
	 */
	public static String genPackageSign(Map<String, String> packageParams) {
		packageParams = ThirdpayUtils.paraFilter(packageParams);
		String paramsStr = ThirdpayUtils.createLinkString(packageParams);
		paramsStr += "&key=" + WeixinPayConfig.KEY;
		logger.info("genPackageSign params : " + paramsStr);
		String packageSign = MD5.getMD5Code(paramsStr).toUpperCase();
		logger.info("genPackageSign signed params : " + packageSign);
		return packageSign;
	}

	/**Map to xml
	 * @param params
	 * @return
	 */
	public static String toXml(Map<String, String> params) {
		StringBuilder sb = new StringBuilder();
		sb.append("<xml>");
		for (Entry<String, String> entry : params.entrySet()) {
			sb.append("<" + entry.getKey() + ">");
			sb.append(entry.getValue());
			sb.append("</" + entry.getKey() + ">");
		}
		sb.append("</xml>");
		return sb.toString();
	}

	
	/**
	 * 验证消息是否是支付宝发出的合法消息
	 * 
	 * @param params
	 *            通知返回来的参数数组
	 * @return 验证结果
	 */
	public static boolean verifyMapParam(Map<String, String> parameterMap,
			String weiXinSign) {
		String sign = genPackageSign(parameterMap);
		logger.info("my sign is:" + sign + ", weixin sign is:" + weiXinSign);
		if (weiXinSign.equals(sign)) {
			return true;
		} else {
			return false;
		}
	}

}
